package org.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.domain.PageInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.component.PayFactory;
import org.example.config.RabbitMQConfig;
import org.example.constants.CacheKey;
import org.example.constants.TimeConstant;
import org.example.enums.*;
import org.example.exception.BizException;
import org.example.feign.AddressFeignService;
import org.example.feign.CouponRecordFeignService;
import org.example.feign.ProductFeignService;
import org.example.interceptor.LoginInterceptor;
import org.example.mapper.ProductOrderItemMapper;
import org.example.mapper.ProductOrderMapper;
import org.example.model.*;
import org.example.request.*;
import org.example.service.ProductOrderService;
import org.example.util.CommonUtil;
import org.example.util.JsonData;
import org.example.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.CollectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderMapper orderMapper;

    @Autowired
    private AddressFeignService addressFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponRecordFeignService couponRecordFeignService;

    @Autowired
    private ProductOrderItemMapper orderItemMapper;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 提交订单
     */
    @Override
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();


        // 判断token有效性
        String token = orderRequest.getToken();
        if (StringUtils.isBlank(token)) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
        }
        // 原子操作，校验令牌，删除令牌
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(String.format(CacheKey.SUBMIT_ORDER_TOKEN, loginUser.getId())),
                token);
        if (result == 0L) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
        }


        Long addressId = orderRequest.getAddressId();
        //生成订单号
        String outTradeNo = CommonUtil.getStringNumRandom(32);
        //根据地址id查询地址
        ProductOrderAddressVO orderAddressVO = this.getUserAddress(addressId);
        log.info("地址: {}", orderAddressVO);
        List<Long> productIds = orderRequest.getProductIds();
        //远程调用product服务，根据productIds查询出购物车项
        JsonData orderItemData = productFeignService.confirmOrderItem(productIds);
//        List<OrderItemVO> orderItemVOList = (List<OrderItemVO>) orderItemData.getData();
        List<OrderItemVO> orderItemVOList = orderItemData.getData(new TypeReference<>() {
        });
        if (orderItemVOList == null) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }
        // 检查前后端的价格，包括使用优惠券后的价格
        this.checkPrice(orderItemVOList, orderRequest);
        // 锁定优惠券记录
        this.lockCouponRecords(orderRequest, outTradeNo);
        // 锁定商品库存
        this.lockProductStocks(orderItemVOList, outTradeNo);
        // 创建商品订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, outTradeNo, loginUser, orderAddressVO);
        this.saveProductItems(productOrderDO.getId(), outTradeNo, orderItemVOList);
        // 发送消息
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(outTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(),
                orderMessage);
        // 创建支付
        PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(), productOrderDO.getPayAmount(), productOrderDO.getPayType(),
                orderRequest.getClientType(), outTradeNo, orderItemVOList.get(0).getProductTitle(), TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
        String payResult = payFactory.pay(payInfoVO);
        if (StringUtils.isNotBlank(payResult)) {
            log.info("创建订单成功: payInfoVO={}, payResult={}", payInfoVO, payResult);
            return JsonData.buildSuccess(payResult);
        } else {
            log.error("创建订单失败: payInfoVO={}, payResult={}", payInfoVO, payResult);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }
    }


    /**
     * 保存商品订单
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, String outTradeNo,
                                            LoginUser loginUser, ProductOrderAddressVO orderAddressVO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setOutTradeNo(outTradeNo);
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        productOrderDO.setPayAmount(orderRequest.getRealPayAmount());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());
        productOrderDO.setNickname(loginUser.getName());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());
        productOrderDO.setReceiverAddress(JSON.toJSONString(orderAddressVO));
        orderMapper.insert(productOrderDO);
        return productOrderDO;
    }

    /**
     * 保存商品订单项
     */
    private void saveProductItems(Long orderId, String outTradeNo, List<OrderItemVO> orderItemVOList) {

        List<ProductOrderItemDO> itemDOList = orderItemVOList.stream().map(obj -> {
            ProductOrderItemDO productOrderItemDO = new ProductOrderItemDO();
            productOrderItemDO.setProductOrderId(orderId);
            productOrderItemDO.setOutTradeNo(outTradeNo);
            productOrderItemDO.setProductId(obj.getProductId());
            productOrderItemDO.setProductName(obj.getProductTitle());
            productOrderItemDO.setProductImg(obj.getProductImg());
            productOrderItemDO.setBuyNum(obj.getBuyNum());
            productOrderItemDO.setCreateTime(new Date());
            productOrderItemDO.setTotalAmount(obj.getTotalAmount());
            productOrderItemDO.setAmount(obj.getAmount());
            return productOrderItemDO;
        }).collect(Collectors.toList());
        //批量保存
        orderItemMapper.insertBatch(itemDOList);
    }

    /**
     * 锁定商品库存
     */
    private void lockProductStocks(List<OrderItemVO> orderItemVOList, String outTradeNo) {
        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOutTradeNo(outTradeNo);
        // 遍历orderItemVOList，设置OrderItemList
        List<OrderItemRequest> orderItemRequestList = orderItemVOList.stream().map(obj -> {
            OrderItemRequest orderItemRequest = new OrderItemRequest();
            orderItemRequest.setProductId(obj.getProductId());
            orderItemRequest.setBuyNum(obj.getBuyNum());
            return orderItemRequest;
        }).collect(Collectors.toList());
        lockProductRequest.setOrderItemList(orderItemRequestList);
        JsonData jsonData = productFeignService.lockProducts(lockProductRequest);
        if (jsonData.getCode() != 0) {
            log.error("锁定商品库存失败: {}", lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     */
    private void lockCouponRecords(ConfirmOrderRequest orderRequest, String outTradeNo) {
        // 优惠券记录列表
        List<Long> couponRecordList = new ArrayList<>();
        if (orderRequest.getCouponRecordId() > 0) {
            couponRecordList.add(orderRequest.getCouponRecordId());
            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setLockCouponRecordIds(couponRecordList);
            lockCouponRecordRequest.setOrderOutTradeNo(outTradeNo);
            // 远程调用锁定优惠券记录
            JsonData jsonData = couponRecordFeignService.lockCouponRecords(lockCouponRecordRequest);
            if (jsonData.getCode() != 0) {
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }
    }

    /**
     * 对比价格
     */
    private void checkPrice(List<OrderItemVO> orderItemVOList, ConfirmOrderRequest orderRequest) {
        // 初始化总价格
        BigDecimal realPayAmount = new BigDecimal("0");
        // 累加总价格
        if (orderItemVOList != null) {
            for (OrderItemVO orderItemVO : orderItemVOList) {
                BigDecimal itemRealPayAmount = orderItemVO.getTotalAmount();
                realPayAmount = realPayAmount.add(itemRealPayAmount);
            }
        }
        // 使用优惠券并且判断是否可用
        CouponRecordVO couponRecordVO = this.getCartCouponRecord(orderRequest.getCouponRecordId());
        if (couponRecordVO != null) {
            //当前总价格小于满减价格时不行
            if (realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0) {
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }
            //优惠券降价大于当前总价格时，总价格置为0，不能为负数
            if (realPayAmount.compareTo(couponRecordVO.getPrice()) < 0) {
                realPayAmount = BigDecimal.ZERO;
            } else {
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }
        }

        // 使用优惠券后，前端和后端比较，真·验价
        if (realPayAmount.compareTo(orderRequest.getRealPayAmount()) != 0) {
            log.error("订单验价失败：{}", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }

    }

    /**
     * 获取满足使用条件的优惠券记录
     *
     * @param couponRecordId
     * @return
     */
    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {
        if (couponRecordId == null || couponRecordId < 0) {
            return null;
        }
        JsonData couponRecordJsonData = couponRecordFeignService.detail(couponRecordId);
        if (couponRecordJsonData.getCode() != 0) {
            log.error("优惠券不存在");
            throw new BizException(BizCodeEnum.COUPON_NO_EXITS);
        }
        if (couponRecordJsonData.getCode() == 0) {
            CouponRecordVO couponRecordVO = couponRecordJsonData.getData(new TypeReference<>() {
            });
            //判断优惠券使用条件是否满足
            Boolean flag = this.couponAvailable(couponRecordVO);
            if (!flag) {
                log.error("优惠券使用失败");
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        }
        return null;
    }

    /**
     * 判断优惠券是否满足使用条件
     */
    private Boolean couponAvailable(CouponRecordVO couponRecordVO) {
        if (couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())) {
            long startTime = couponRecordVO.getStartTime().getTime();
            long endTime = couponRecordVO.getEndTime().getTime();
            long currentTime = CommonUtil.getCurrentTimestamp();
            if (currentTime >= startTime && currentTime <= endTime) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据地址id查询地址
     */
    private ProductOrderAddressVO getUserAddress(Long addressId) {
        JsonData jsonData = addressFeignService.detail(addressId);
        if (jsonData.getCode() != 0) {
            log.error("获取地址失败: {}", jsonData);
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }
        ProductOrderAddressVO orderAddressVO = jsonData.getData(new TypeReference<ProductOrderAddressVO>() {
        });
        log.info("获取地址成功: {}", orderAddressVO);
        return orderAddressVO;
    }

    /**
     * 查询订单状态
     */
    @Override
    public String queryOrderState(String outTradeNo) {
        ProductOrderDO productOrderDO = orderMapper.selectOne(new QueryWrapper<ProductOrderDO>()
                .eq("out_trade_no", outTradeNo));
        if (productOrderDO == null) {
            return "";
        }
        return productOrderDO.getState();
    }

    /**
     * 定时关单，要判断订单是否支付成功
     */
    @Override
    public boolean closeOrder(OrderMessage orderMessage) {
        ProductOrderDO productOrderDO = orderMapper.selectOne(new QueryWrapper<ProductOrderDO>()
                .eq("out_trade_no", orderMessage.getOutTradeNo()));
        if (productOrderDO == null) {
            log.info("订单不存在，消费消息: {}", orderMessage);
            return true;
        }

        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            log.info("已支付成功，消费消息: {}", orderMessage);
            return true;
        }

        // 向第三支付方查询支付结果:payResult
        PayInfoVO payInfoVO = new PayInfoVO();
        payInfoVO.setPayType(productOrderDO.getPayType());
        payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryPaySuccess(payInfoVO);

        // 支付状态为NEW时，如果payResult为空，则从NEW改为CANCEL，订单取消
        if (StringUtils.isBlank(payResult)) {
            log.info("payResult为空，订单取消: {}", orderMessage);
            orderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(),
                    ProductOrderStateEnum.NEW.name());
            return true;
        } else {
            // 支付状态为NEW时，如果payResult不为空，则从NEW改为PAY，订单支付成功
            log.info("payResult不为空，订单有NEW改为PAY: {}", orderMessage);
            orderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.PAY.name(),
                    ProductOrderStateEnum.NEW.name());
            return true;
        }

    }

    /**
     * 支付结果回调
     */
    @Override
    public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {
        // 支付宝支付
        if (ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType.name())) {
            String outTradeNo = paramsMap.get("out_trade_no");
            String tradeStatus = paramsMap.get("trade_status");
            if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISH".equalsIgnoreCase(tradeStatus)) {
                orderMapper.updateOrderPayState(outTradeNo,
                        ProductOrderStateEnum.NEW.name(),
                        ProductOrderStateEnum.PAY.name());
                return JsonData.buildSuccess();
            }
        } else if (ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType.name())) {
            // 微信支付 TODO
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 根据支付状态分页查询我的订单
     */
    @Override
    public PageResult getPageByState(Integer page, Integer size, String state) {
        Long userId = LoginInterceptor.threadLocal.get().getId();
        Page<ProductOrderDO> pageInfo = new Page<>(page, size);
        IPage<ProductOrderDO> orderDOIPage = null;
        // 判断state，为空时直接查询全部
        if (StringUtils.isBlank(state)) {
            orderDOIPage = orderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderDO>().eq("user_id", userId));
        } else {
            orderDOIPage = orderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderDO>().eq("user_id", userId)
                    .eq("state", state));
        }
        List<ProductOrderDO> productOrderDOList = orderDOIPage.getRecords();
        if (productOrderDOList.isEmpty()) {
            return null;
        }
        // 遍历查询orderVOList
        List<ProductOrderVO> orderVOList = productOrderDOList.stream().map(orderDO -> {
            ProductOrderVO productOrderVO = new ProductOrderVO();
            BeanUtils.copyProperties(orderDO, productOrderVO);
            List<ProductOrderItemDO> orderItemDOList = orderItemMapper.selectList(new QueryWrapper<ProductOrderItemDO>()
                    .eq("product_order_id", orderDO.getId()));
            //将OrderItemDO转化为OrderItemVO
            List<OrderItemVO> itemVOList = orderItemDOList.stream().map(item -> {
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(item, orderItemVO);
                orderItemVO.setProductTitle(item.getProductName());
                return orderItemVO;
            }).collect(Collectors.toList());
            productOrderVO.setOrderItemVOList(itemVOList);
            return productOrderVO;
        }).collect(Collectors.toList());

        return new PageResult(orderDOIPage.getTotal(), orderDOIPage.getPages(), orderVOList);
    }

    /**
     * 二次支付
     */
    @Override
    public JsonData repay(RepayOrderRequest repayOrderRequest) {
        Long userId = LoginInterceptor.threadLocal.get().getId();
        String outTradeNo = repayOrderRequest.getOutTradeNo();
        // 查询订单是否存在
        ProductOrderDO productOrderDO = orderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", outTradeNo)
                .eq("user_id", userId));
        if (productOrderDO == null) {
            return JsonData.buildResult(BizCodeEnum.ORDER_CONFIRM_NOT_EXIST);
        }
        // 判断订单状态（只能为NEW）
        if (!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        }
        // 判断订单是否超时
        // 1)订单存活时间,现在时间-创建时间,多加1分钟,提前1分钟不让支付
        long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime() + 60 * 1000;
        if (TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime > 0) {
            // 调用支付
            long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
            PayInfoVO payInfoVO = new PayInfoVO(outTradeNo, productOrderDO.getPayAmount(), productOrderDO.getPayType(),
                    repayOrderRequest.getClientType(), outTradeNo, "", timeout);
            String payResult = payFactory.pay(payInfoVO);
            // 判断是否支付成功
            if (StringUtils.isNotBlank(payResult)) {
                log.info("二次支付成功! payInfo: {}, payResult: {}", payInfoVO, payResult);
                return JsonData.buildSuccess(payResult);
            } else {
                log.info("二次支付失败! payInfo: {}, payResult: {}", payInfoVO, payResult);
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
            }
        } else {
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
        }
    }

}
